#include "tcp_server.h"

#include "time_wheel_handler.h"

namespace qio {

TcpServer::~TcpServer() QIO_NOEXCEPT {
  delete[] conn_read_buffer_;
  delete acceptor_;
}

TcpServer::TcpServer(EventLoop *loop, const Config &conf) QIO_NOEXCEPT
    : loop_(loop),
      acceptor_(nullptr),
      server_config_(conf),
      conn_read_buffer_len_(server_config_.buffer_size),
      conn_read_buffer_(new char[conn_read_buffer_len_]),
      conn_establish_cb_(nullptr),
      conn_disconn_cb_(nullptr),
      conn_msg_cb_(nullptr),
      time_wheel_(loop_) {
  assert(loop_);
}

Fd CreateServerSocket(const TcpServer::Config &conf) QIO_NOEXCEPT {
  Fd accept_fd = socket_create(AF_INET, SOCK_STREAM, IPPROTO_IP);
  if (conf.reuse_addr) {
    SetReuseAddr(accept_fd);
  }
  if (conf.reuse_port) {
    SetReusePort(accept_fd);
  }
  struct sockaddr_in bind_sock_addr {};
  IPNameToSockAddr4(conf.address.addr, conf.address.port, &bind_sock_addr);
  socket_bind(accept_fd, reinterpret_cast<const sockaddr *>(&bind_sock_addr),
              sizeof bind_sock_addr);
  socket_listen(accept_fd, conf.backlog);
  return accept_fd;
}

void TcpServer::Start() QIO_NOEXCEPT {
  loop_->RunInLoop([this]() {
    // start time wheel
    time_wheel_.StartTimeout(server_config_.rd_timeout);
    //
    const Fd accept_fd = CreateServerSocket(server_config_);
    assert(accept_fd > 0);  // NOLINT

    auto &ssl_helper = server_config_.ssl_helper;
    TcpAcceptor::UniqueConfig acceptor_conf(new TcpAcceptor::Config);
    // ssl property
    if (ssl_helper) {
      acceptor_conf->ssl_helper = ssl_helper;
    }
    // acceptor conn_callbacks
    auto &acceptor_callbacks = acceptor_conf->acceptor_callbacks;
    acceptor_callbacks.close_cb =
        std::bind(&TcpServer::HandleServerClosed, this, std::placeholders::_1);
    acceptor_callbacks.error_cb =
        std::bind(&TcpServer::HandleAcceptorError, this, std::placeholders::_1);
    // conn conn_callbacks
    auto &conn_callbacks = acceptor_conf->conn_callbacks;
    conn_callbacks.establish_cb = conn_establish_cb_;
    conn_callbacks.disconn_cb = conn_disconn_cb_;
    conn_callbacks.msg_cb = conn_msg_cb_;
    // basic property
    acceptor_conf->fd = accept_fd;
    acceptor_conf->io_buffer = conn_read_buffer_;
    acceptor_conf->io_buffer_len = conn_read_buffer_len_;
    acceptor_conf->timeout_generator = std::bind(
        &TcpServer::GenerateTimeoutHandle, this, std::placeholders::_1);
    if (!server_config_.buffer_generator) {
      acceptor_conf->buffer_generator =
          std::bind(&TcpServer::GenerateUniqueBuffer, this);
    }
    acceptor_ = new TcpAcceptor(loop_, std::move(acceptor_conf));
    acceptor_->Start();
  });
}

void TcpServer::Stop() QIO_NOEXCEPT {
  loop_->RunInLoop([this]() { acceptor_->Close(); });
}

void TcpServer::HandleAcceptorError(TcpAcceptor *) QIO_NOEXCEPT {}

void TcpServer::HandleServerClosed(TcpAcceptor *) QIO_NOEXCEPT {
  // server closed
  printf("TcpServer::HandleServerClosed\n");
}

SharedTimeoutHandle TcpServer::GenerateTimeoutHandle(const SharedTcpConn &conn)
    QIO_NOEXCEPT {
  if (conn == nullptr) {
    return nullptr;
  }
  auto hd = std::make_shared<TimeWheelHandle>(&time_wheel_, conn);
  auto xhd = hd->GetSharedHandle();
  return xhd;
}

UniqueBuffer TcpServer::GenerateUniqueBuffer() const QIO_NOEXCEPT {
  return std::make_unique<ChanBuffer>();
}

}  // namespace qio